// reconciliation/index.js 和解、对账

import { updateNodeElement } from "../Dom"
import { arrified, createStateNode, createTaskQueue, getRoot, getTag } from "../Misc"

const taskQueue = createTaskQueue()
let subTask = null
let pendingCommit = null

// 进程跟新
export function scheduleUpdate (instance, partialState) {
  taskQueue.push({
    // 标识
    from: 'class_component',
    instance,
    partialState
  })
  requestIdleCallback(performTask)
}

// 渲染dom
function commitAllWork (rootFiber) {
  rootFiber.effects.forEach(fiber => {
    //类组件更新状态
    if (fiber.tag === 'class_component') {
      fiber.stateNode.__fiber = fiber
    }

    if (fiber.effectTag === 'placement') {
      let parentFiber = fiber.parent

      // 类组件，一直循环找上级非类组件，然后追加子节点
      while (parentFiber.tag === 'class_component' || parentFiber.tag === 'function_component') {
        parentFiber = parentFiber.parent
      }

      fiber.tag === 'host_component' && parentFiber.stateNode.appendChild(fiber.stateNode)
    } else if (fiber.effectTag === 'update') {//更新
      if (fiber.type === fiber.alternate.type) {//类型相同
        updateNodeElement(fiber.stateNode, fiber, fiber.alternate)
      } else {
        fiber.parent.stateNode.replaceChild(fiber.stateNode, fiber.alternate.stateNode)
      }
    } else if (fiber.effectTag === 'delete') {
      fiber.parent.stateNode.removeChild(fiber.stateNode)
    }
  })

  /**
   * 备份旧fiber对象，实现更新节点
   */
  rootFiber.stateNode.__rootFiberContainer = rootFiber
}

// 构建子节点
function reconcileChildren (fiber, children) {//使一致
  /**
   * 1. createElement的children 是数组
   * 2. 也可能是vdom对象
   */
  if (fiber) {
    const arrifiedChildren = arrified(children)
    let newFiber = null
    let prevFiber = null //上一个节点
    let alternate = null //要替换的备份
    let index = 0
    let element = null

    if (fiber.alternate && fiber.alternate.child) {//有备份和备份子节点
      alternate = fiber.alternate.child //重新备份第一个子节点
    }

    while (index < arrifiedChildren.length || alternate) {
      element = arrifiedChildren[index]
      /**
      * 新旧节点的fiber修改
      */
      if (!element && alternate) {//删除节点
        alternate.effectTag = "delete"
        fiber.effects.push(alternate)
      } else if (element && alternate) {//有备份更新
        newFiber = {
          type: element.type,
          props: element.props,
          tag: getTag(element), //? 普通节点
          effects: [],
          effectTag: 'update', //* 追加节点placement 、更新 update
          parent: fiber,
          alternate
        }
        /**
         * 新旧节点的类型对比
         */
        if (element.type === alternate.type) {//类型相同
          newFiber.stateNode = alternate.stateNode
        } else { //类型不同
          newFiber.stateNode = createStateNode(newFiber)
        }

      } else if (element && !alternate) {//没有备份节点，初始渲染
        newFiber = {
          type: element.type,
          props: element.props,
          tag: getTag(element), //? 普通节点
          effects: [],
          effectTag: 'placement', //追加节点placement 、更新 update
          parent: fiber
        }
        newFiber.stateNode = createStateNode(newFiber)
      }

      /**
     * 1. 第一个子节点，给父节点赋值
     * 2. 第二个子节点开始，给上一节点设置兄弟节点
     */
      if (index === 0) {
        fiber.child = newFiber
      } else if (element) {
        prevFiber.sibling = newFiber
      }

      if (alternate && alternate.sibling) {// 备份节点的兄弟节点有，则更新备份节点
        alternate = alternate.sibling
      } else {
        alternate = null //没有兄弟节点，备份置空
      }

      prevFiber = newFiber//下次循环备用
      index++
    }

  }

}

// 执行子任务
function executeTask (fiber) {
  // 类组件子节点，是render方法调用
  let fiberChildren = fiber.props.children
  if (fiber.tag === 'class_component') {

    // 组件更新状态
    const rootClassFiber = fiber.stateNode.__fiber
    if (rootClassFiber && rootClassFiber.partialState) {
      fiber.stateNode.state = {
        ...fiber.stateNode.state,
        ...rootClassFiber.partialState
      }
    }

    fiberChildren = fiber.stateNode.render()
  }
  fiber.tag === 'function_component' && (fiberChildren = fiber.stateNode(fiber.props))
  reconcileChildren(fiber, fiberChildren)

  // 有子节点，作为下一个任务的父节点
  if (fiber.child) {
    return fiber.child
  } else {
    // 没有子节点，看兄弟节点，没有兄弟返回父级节点（递归）
    let curFiber = fiber
    while (curFiber.parent) {//直到根节点停止

      // 从上到下合并，收集所有子fiber
      curFiber.effects = curFiber.effects.concat(
        [curFiber]
      )
      curFiber.parent.effects = curFiber.parent.effects.concat(
        curFiber.effects
      )

      if (curFiber.sibling) {
        // 处理兄弟节点
        return curFiber.sibling
      } else {
        // 返回父节点，处理兄弟节点
        curFiber = curFiber.parent
      }
    }

    pendingCommit = curFiber
    commitAllWork(pendingCommit)
  }
}

// 获取子任务
function getFirstTask () {
  const task = taskQueue.pop()

  if (task.from === 'class_component') {
    const root = getRoot(task.instance)
    task.instance.__fiber.partialState = task.partialState
    return {
      props: root.props,
      stateNode: root.stateNode,
      tag: 'host_root', //根元素
      effects: [],
      child: null,
      alternate: root//要替换的备份
    }
  }
  return {
    props: task.props,
    stateNode: task.dom,
    tag: 'host_root', //根元素
    effects: [],
    child: null,
    alternate: task.dom.__rootFiberContainer//要替换的备份
  }
}

function workLoop (deadline) {
  if (!subTask) {
    // 赋值第一个子任务
    subTask = getFirstTask()
  }

  while (subTask && deadline.timeRemaining() > 1) {
    /**
     * 1. executeTask执行，接受，返回新任务
     * 2. 循环中重新赋值子任务，没有任务就结束循环
     */
    subTask = executeTask(subTask)//执行
  }
}

// 执行任务
function performTask (deadline) {
  // 循环
  workLoop(deadline)

  if (subTask || !taskQueue.isEmpty()) {
    requestIdleCallback(performTask)
  }
}

export function render (element, dom) {
  /**
   * 1. 任务队列中添加任务（vdom变为fiber）
   * 2. 浏览器空闲时执行任务
   */
  taskQueue.push({
    dom,
    props: {
      children: element
    }
  })

  requestIdleCallback(performTask)
}